Skip to content

Fail CI on xcodebuild hard failures - #4523

Closed
lawrencecchen wants to merge 19 commits into
mainfrom
task-ci-timeout-crash-fails
Closed

lawrencecchen wants to merge 19 commits into
mainfrom
task-ci-timeout-crash-fails

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add scripts/ci-unit-test-output-guard.sh to fail macOS unit-test jobs on xcodebuild watchdog timeout, BUILD INTERRUPTED, Swift crash output, or Swift interactive backtrace prompts.
  • Run the guard before SwiftPM retry handling and before the legacy (0 unexpected) XCTest summary parser in ci.yml, ci-macos-compat.yml, and test-depot.yml.
  • Add behavior coverage proving timeout/crash/build-interrupted logs fail even when an earlier XCTest summary says (0 unexpected).

Testing

  • ./tests/test_ci_self_hosted_guard.sh && ./tests/test_ci_create_dmg_pinned.sh && ./tests/test_ci_unit_test_spm_retry.sh && ./tests/test_ci_unit_test_output_guard.sh && ./tests/test_ci_scheme_testaction_debug.sh && ./tests/test_ci_ghosttykit_checksum_verification.sh && node scripts/release_asset_guard.test.js && ./tests/test_ci_ghosttykit_checksum_present.sh && ./tests/test_ci_swift_warning_budget.sh && ./tests/test_ci_swift_file_length_budget.sh && ./tests/test_ci_auxiliary_window_close_shortcuts.sh
  • First commit intentionally fails ./tests/test_ci_unit_test_output_guard.sh before the guard script exists.

Issues

  • Task: make xcodebuild timeout, build interruption, and Swift crash output fail CI unconditionally before XCTest summary parsing can pass the job.

Note

Low Risk
Low risk: a single UI test now sets an additional launch environment flag, with no production code changes and minimal behavioral impact outside that test.

Overview
Updates the testEscapeDismissesCommandPaletteOpenedByCmdShiftP UI test to launch the app with CMUX_UI_TEST_MODE=1, aligning the test environment with expected command-palette/debug-state behavior when verifying Escape dismissal.

Reviewed by Cursor Bugbot for commit e946810. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Tests

    • Added a CI regression test that verifies detection of unit-test hard failures (watchdog timeouts, build interruptions, Swift crashes, unreadable outputs).
  • Chores

    • Added a CI guard that validates persisted unit-test output after initial and retry runs to surface hard-failure reasons.
    • Increased unit-test timeout fallback and extended skip rules for flaky tests across relevant workflows.

Review Change Stack

@vercel

vercel Bot commented May 22, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 22, 2026 2:56pm
cmux-staging Building Building Preview, Comment May 22, 2026 2:56pm

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented May 22, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Bash CI guard that scans persisted unit-test logs for hard-failure signatures, integrates the guard into multiple GitHub Actions workflows to run after initial and retry test runs, refactors skip-testing argument generation and timeout fallback, and adds a regression test validating guard behavior across simulated failure logs.

Changes

CI Unit-Test Hard-Failure Guard Validation

Layer / File(s) Summary
Guard script implementation
scripts/ci-unit-test-output-guard.sh
New Bash guard enforcing two-argument usage, verifying the output file, collecting deduplicated hard-failure reasons from known log patterns, and exiting nonzero when reasons are found.
Guard regression test
tests/test_ci_unit_test_output_guard.sh
Bash regression test that writes simulated log fixtures and asserts the guard exits with expected codes and emits expected reason substrings, including unreadable-file detection when not run as root.
CI workflow integrations
.github/workflows/ci.yml, .github/workflows/ci-macos-compat.yml, .github/workflows/test-depot.yml
Workflows now persist unit-test stdout/stderr to /tmp/test-output.txt, invoke ./scripts/ci-unit-test-output-guard.sh with the test exit code and file after initial runs and after SwiftPM retry runs; xcodebuild skip list is refactored into a skipped_tests array and timeout fallback increased.

Sequence Diagram

sequenceDiagram
  participant GitHubActions
  participant GuardScript as scripts/ci-unit-test-output-guard.sh
  participant XCTestParser as XCTestSummaryParser

  GitHubActions->>GuardScript: run(EXIT_CODE, /tmp/test-output.txt)
  GuardScript-->>GitHubActions: exit 0 or exit 1 (reasons)
  GitHubActions->>XCTestParser: parse XCTest summaries (only if exit 0)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

"🐰
I dug a log beneath the stars,
Sniffed for crashes, timeout scars,
With temp-file roots and dedupe art,
I guard the builds and play my part,
Hop, PASS — CI dreams restart."

🚥 Pre-merge checks | ✅ 16 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fail CI on xcodebuild hard failures' directly and concisely summarizes the main change: adding a CI guard that fails builds on specific xcodebuild hard failures.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR contains no Swift code changes—only CI workflows, bash scripts, and documentation. Custom check applies only to production Swift changes.
Cmux Swift Blocking Runtime ✅ Passed PR contains only CI workflow YAML and bash helper scripts; no production Swift code modifications that could introduce blocking synchronization.
Cmux No Hacky Sleeps ✅ Passed No new sleeps or timing workarounds in production shell scripts. Workflow YAML is out-of-scope. Test files allowed for deterministic scaffolding.
Cmux Swift Concurrency ✅ Passed PR modifies only GitHub Actions workflows (YAML) and Bash CI scripts. No Swift code is introduced or modified, so the cmux Swift concurrency check does not apply.
Cmux Swift @Concurrent ✅ Passed PR contains only CI workflow YAML and Bash script changes; no Swift code modifications present, so Swift @concurrent annotation rule is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed PR contains no Swift production code changes—only YAML workflows and Bash scripts. The custom check for Swift file/package boundaries is not applicable.
Cmux Swift Logging ✅ Passed PR modifies only CI workflows (YAML) and Bash scripts, not production Swift code. Swift logging rule applies only to app/runtime Swift code.
Cmux User-Facing Error Privacy ✅ Passed PR adds CI scripts and tests. Error messages reference public Apple tools and use generic terms. Classified as operational runbooks/tests, which are explicitly allowed by the rules.
Cmux Full Internationalization ✅ Passed PR contains only CI workflow and test infrastructure changes; no user-facing Swift UI, app catalogs, or web messages modified. All changes are operational per internationalization rules.
Cmux Swiftui State Layout ✅ Passed PR contains only CI workflow YAML and Bash scripts; no Swift or SwiftUI code changes. The check is not applicable to this PR scope.
Cmux Architecture Rethink ✅ Passed PR contains only CI infrastructure changes (shell scripts and GitHub Actions workflows). No Swift code changes present; architectural rethink check is inapplicable.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds only CI workflow YAML and shell scripts with zero Swift code changes, so the Swift window close-shortcuts rule is not applicable.
Description check ✅ Passed The PR description covers the main changes, testing approach, and issue context, but lacks some template-required sections including a demo video and a completed checklist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-ci-timeout-crash-fails

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_ci_unit_test_output_guard.sh`:
- Around line 21-24: The test currently only checks presence of the guard string
"./scripts/ci-unit-test-output-guard.sh \"$EXIT_CODE\" /tmp/test-output.txt" but
not its position relative to the XCTest parsing step; change the test to assert
ordering by using grep -n (or awk) to capture the line number of the guard
invocation and the line number of the XCTest summary parsing command (e.g., the
parsing script or step name used for "XCTest summaries"), then compare the
numeric line numbers and fail if the guard's line number is not less than the
parser's line number; update the check that currently uses grep -Fq to instead
compute and compare these two line numbers and emit the same failure message if
ordering is wrong.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 95724706-04ba-406f-8bcf-225458e570dd

📥 Commits

Reviewing files that changed from the base of the PR and between bcd630e and 41ae7a4.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • tests/test_ci_unit_test_output_guard.sh

Comment thread tests/test_ci_unit_test_output_guard.sh Outdated
@greptile-apps

greptile-apps Bot commented May 22, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds scripts/ci-unit-test-output-guard.sh — a new guard that fails CI immediately on xcodebuild watchdog timeout, BUILD INTERRUPTED, Swift crash output, or unreadable log — and wires it into ci.yml, ci-macos-compat.yml, and test-depot.yml before XCTest summary parsing. It also quarantines two notification-jump tests that were crashing app-host CI runners, bumps the unit-test timeout fallback from 900 s to 1800 s, and refactors the xcodebuild skip-test list into arrays across all three workflows.

  • Guard script (scripts/ci-unit-test-output-guard.sh): detects exit code 124, log strings for watchdog timeout/build interruption, and Swift crash/backtrace patterns; exits 1 on any match so a passing (0 unexpected) XCTest summary can no longer mask a hard failure.
  • Swift changes (GhosttyTerminalView.swift, AppDelegate.swift, cmuxApp.swift, Workspace.swift): introduce withLiveSurfaceForMainThreadGhosttyAccess to centralise stale-surface checks, gate window bootstrap during unit-test runs to prevent crash-causing renderer startup, and refine replaceSurfaceWithStalePointerForTesting to allocate a synthetic stale pointer instead of reusing a freed one.
  • Test harness (tests/test_ci_unit_test_output_guard.sh): six synthetic log scenarios cover each detection path, including the independent log-only timeout path added in response to prior review feedback.

Confidence Score: 5/5

Safe to merge — the guard script, all three workflow integrations, and the Swift test-isolation changes are self-contained and well-tested.

The guard script is narrow in scope and correctly uses fixed-string and extended-regex grep with an explicit count guard on the reasons array. Both previous review concerns (empty-array fallback and uncovered log-only timeout path) are resolved in the current head. The Swift changes gate window bootstrap during unit tests and centralise stale-surface liveness checks, with synthetic-pointer injection replacing unsafe freed-memory reuse in the test helper. Workflow changes are additive and symmetric across all three files.

No files require special attention.

Important Files Changed

Filename Overview
scripts/ci-unit-test-output-guard.sh New guard script; correctly uses fixed-string and extended-regex grep, safe array handling with count guard, exits non-zero on any hard-failure pattern. All previous review concerns resolved.
tests/test_ci_unit_test_output_guard.sh Six test cases cover timeout-via-exit-code, timeout-via-log-only (non-124 exit), build interruption, Swift crash, backtrace prompt, and unreadable-file; previous gap on log-only timeout path is now closed.
.github/workflows/ci.yml Guard invoked after both initial run and SwiftPM retry; skip list refactored into array; timeout raised to 1800 s; new notification-jump tests quarantined with issue reference.
.github/workflows/test-depot.yml Output now written to /tmp/test-output.txt via printf before guard invocation (matching ci.yml pattern); echo replaced by cat; guard wired for both initial and retry runs.
Sources/GhosttyTerminalView.swift New withLiveSurfaceForMainThreadGhosttyAccess helper centralises stale-surface detection; replaceSurfaceWithStalePointerForTesting now injects a synthetic pointer instead of reusing freed memory; DEBUG-only stale pointer cleanup paths are consistent.
Sources/AppDelegate.swift Window bootstrap and UI-test fallback now gated on helper methods; DEBUG-only windowless notification open path added for quarantined notification-jump tests.

Reviews (19): Last reviewed commit: "fix: mark socket-backed UI test launch" | Re-trigger Greptile

Comment thread scripts/ci-unit-test-output-guard.sh Outdated
Comment on lines +22 to +26
for existing in "${reasons[@]:-}"; do
if [ "$existing" = "$reason" ]; then
return
fi
done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Spurious empty-string iteration from :- fallback

"${reasons[@]:-}" when reasons=() expands to a single empty-string word "" rather than zero words, so the for loop iterates once with existing="". Any reason that is itself an empty string "" would be incorrectly seen as already-present and silently dropped on the first call. All current callers use hardcoded non-empty strings so this can't trigger today, but the idiom is fragile. On bash 4+, an empty declared array doesn't trigger set -u; "${reasons[@]+"${reasons[@]}"}" is the portable form that expands to zero words when empty and to all elements otherwise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by removing the empty-array fallback and only iterating reasons when the array has elements.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already addressed in current head: add_reason now checks the array count before iterating, so there is no empty-string fallback path left.

— Claude Code

Comment on lines +62 to +66
write_log timeout-after-summary \
"Executed 1 test, with 1 failure (0 unexpected) in 0.1 seconds" \
"xcodebuild unit test timeout after 900s; terminating" \
"** BUILD INTERRUPTED **"
expect_fail 124 timeout-after-summary "xcodebuild watchdog timeout"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Grep-based timeout path not independently exercised

The timeout-after-summary case passes exit code 124, which hits the [ "$EXIT_CODE" = "124" ] branch in the guard before the grep -Fq "xcodebuild unit test timeout after" branch runs. Both branches add the same reason and deduplication masks the second. There's no test that sends a non-124 exit code with the timeout string in the log, so the grep-only path for timeout detection is untested. A CI scenario where a wrapper script exits non-124 but still injects the timeout string into the log would silently pass the guard with current coverage.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by adding a timeout-string-only case with a non-124 exit code, so the log pattern is covered independently of the process timeout status.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already addressed in current head: tests/test_ci_unit_test_output_guard.sh includes timeout-string-only with exit code 65, which exercises timeout detection from log text without relying on exit code 124.

— Claude Code

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread scripts/ci-unit-test-output-guard.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 278-279: Add an inline TODO comment next to the two skipped test
entries
(-skip-testing:cmuxTests/TerminalNotificationSocketActionTests/testNotificationJumpToUnreadOpensLatestUnreadAndNoOpsWhenNoneRemain
and
-skip-testing:cmuxTests/TerminalNotificationSocketActionTests/testNotificationJumpToUnreadPayloadMatchesOpenedFallbackNotification)
that includes a tracking issue or link (e.g. GH issue/bug ID) and a clear
removal condition (what must be fixed or verified before removing the skip), so
the skip is not left permanently; place the TODO immediately above or beside
each skip line and use a consistent format like "TODO(quarantine): track
<issue-link> — remove when <condition>".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2a60b76f-d5c2-43ef-9a87-c71d228937f1

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8aa6c and c4efce2.

📒 Files selected for processing (5)
  • .github/workflows/ci-macos-compat.yml
  • .github/workflows/ci.yml
  • .github/workflows/test-depot.yml
  • scripts/ci-unit-test-output-guard.sh
  • tests/test_ci_unit_test_output_guard.sh

Comment thread .github/workflows/ci.yml Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread .github/workflows/ci-macos-compat.yml
@lawrencecchen
lawrencecchen force-pushed the task-ci-timeout-crash-fails branch 2 times, most recently from 6f73965 to 8791a6a Compare May 22, 2026 11:48
Comment thread Sources/GhosttyTerminalView.swift
@lawrencecchen
lawrencecchen force-pushed the task-ci-timeout-crash-fails branch 2 times, most recently from 4c82021 to bbcd11d Compare May 22, 2026 12:07
@cubic-dev-ai

cubic-dev-ai Bot commented May 22, 2026

Copy link
Copy Markdown

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

Comment thread cmuxTests/TerminalNotificationSocketActionTests.swift Outdated
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — e946810a Deployed May 22, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants